package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.SkuPriceVo;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.ProductSpecMapper;
import com.spzx.product.query.SkuQuery;
import com.spzx.product.service.IProductSkuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.product.vo.ProductSkuVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 商品sku 服务实现类
 * </p>
 *
 * @author gax
 * @since 2025-08-23
 */
@Service
@Slf4j
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper, ProductSku> implements IProductSkuService {
    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private DefaultRedisScript<Long> unlockScript;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public List<ProductSkuVo> selectTop20Sale() {
        return productSkuMapper.selectTop20Sale();
    }

    @Override
    public List<ProductSkuVo> selectSkuList(SkuQuery skuQuery) {
        return productSkuMapper.selectSkuList(skuQuery);
    }

    // 根据商品 ID 查询实时价格
    @Override
    public SkuPriceVo getSkuPrice(Long skuId) {
        ProductSku productSku = this.getOne(new LambdaQueryWrapper<ProductSku>()
                .eq(ProductSku::getId, skuId)
                .select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        // 封装价格的 vo 类
        if (productSku != null) {
            SkuPriceVo skuPriceVo = new SkuPriceVo();
            skuPriceVo.setSkuId(skuId);
            skuPriceVo.setSalePrice(productSku.getSalePrice());
            skuPriceVo.setMarketPrice(productSku.getMarketPrice());
            return skuPriceVo;
        }
        return null;
    }

    @Override
    public ProductSku getSkuInfo(Long skuId) {
        // 1 优先从 Redis 获取业务数据，命中则直接返回
        // 1.1 构建业务数据缓存 key，形式 sku:info:skuId
        String dataKey = "sku:info:" + skuId;
        // 1.2 查询 Redis 缓存数据
        ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
        // 1.3 查询结果返回
        if (productSku != null) {
            return productSku;
        }
        // 2 尝试获取分布式锁，避免大量请求同时访问 DB，造成 DB 压力过载宕机
        // 2.1 构建锁 Key，注意，业务数据的 key，将来可能需要缓存几个小时，但是锁的 key，用完就会立刻释放
        String lockName = dataKey + ":lock";
        // 2.2 构建当前线程唯一标识
        String lockValue = UUID.randomUUID().toString();
        // 2.3 尝试获取锁 --- 使用 Redis 的 set nx ex命令即可
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockName, lockValue, 5, TimeUnit.SECONDS);
        // 3 获取锁成功，执行查询数据库业务
        if (flag) {
            try {
                // 3.1 执行查询数据库操作
                productSku = getById(skuId);
                // 3.2 缓存数据到 Redis
                // 3.2.1 为了防止缓存雪崩，设置过期时间为基础时间 + 随机时间，避免同一个时刻大量缓存同时失效
                int ttl = 3600 + new Random().nextInt(600);
                redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.SECONDS);
                // 顺序是先执行 finally 再 return
                return productSku;
            } finally {
                // 4 业务执行完毕，采用 lua 脚本释放锁
                if (redisTemplate.opsForValue().get(lockName).equals(lockValue)) {
                    redisTemplate.execute(unlockScript,Collections.singletonList(lockName),lockValue);
                }
            }
        } else {
            // 5 获取锁失败，则重试
            log.error("获取锁失败：{}，再次尝试获取：", Thread.currentThread().getName());
            // 暂停毫秒
            try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) { e.printStackTrace();};
            return this.getSkuInfo(skuId);
        }
    }

    @Override
    public Map<String, Long> getSkuSpecValue(Long productId) {
        // 创建保存映射关系的 Map
        HashMap<String, Long> specMap = new HashMap<>();
        // 根据商品 id，查询商品的 sku 列表
        List<ProductSku> productSkuList = this.list(new LambdaQueryWrapper<ProductSku>()
                .eq(ProductSku::getProductId, productId)
                .select(ProductSku::getSkuSpec, ProductSku::getId));
        // 遍历 sku 列表，将 sku 规格参数跟 skuid 封装到 map 中
        if (!CollectionUtils.isEmpty(productSkuList)) {
            productSkuList.forEach(elm -> specMap.put(elm.getSkuSpec(), elm.getId()));
        }
        return specMap;
    }

    @Override
    public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
        // 根据多个 skuid 查询商品的 sku 列表
        List<ProductSku> productSkuList = list(
                new LambdaQueryWrapper<ProductSku>().in(ProductSku::getId, skuIdList));
        // 封装实时商品价格 vo 列表
        if (!CollectionUtils.isEmpty(productSkuList)) {
            // 映射：productSku -> skuPriceVo
            List<SkuPriceVo> skuPriceVoList = productSkuList.stream().map(productSku -> {
                SkuPriceVo skuPriceVo = new SkuPriceVo();
                skuPriceVo.setSkuId(productSku.getId());
                skuPriceVo.setSalePrice(productSku.getSalePrice());
                skuPriceVo.setMarketPrice(productSku.getMarketPrice());
                return skuPriceVo;
            }).toList();
            return skuPriceVoList;
        }
        return null;
    }
}